<?php

namespace utils;

use think\facade\Cache;

class HashID
{
    /**
     * 生成团队或企业ID
     * 包含随机字符串、时间戳和机器码，全部小写，不含-_字符
     *
     * @param int $length ID长度，默认24位
     * @return string 指定长度的ID
     */
    public static function generate(int $length = 24): string
    {
        $id = '';
        $attempts = 0;
        $maxAttempts = 10; // 最大尝试次数，避免无限循环

        do {
            // 获取时间戳（取部分）
            $timestamp = dechex(time());

            // 获取机器码（基于服务器信息）
            $machineCode = substr(md5(php_uname('n') . php_uname('m')), 0, 6);

            // 生成随机字符串
            $randomString = substr(md5(uniqid(mt_rand(), true) . mt_rand()), 0, $length - strlen($timestamp) - strlen($machineCode));

            // 组合所有部分
            $id = $timestamp . $machineCode . $randomString;

            // 如果长度不够，补充随机字符
            if (strlen($id) < $length) {
                $remaining = $length - strlen($id);
                $id .= substr(md5(uniqid()), 0, $remaining);
            }
            // 如果长度超出，截取到指定长度
            elseif (strlen($id) > $length) {
                $id = substr($id, 0, $length);
            }

            // 确保全部小写
            $id = strtolower($id);

            $attempts++;

            // 如果尝试次数过多，调整策略
            if ($attempts > 5) {
                // 在ID末尾添加额外的随机数以确保唯一性
                $id = substr($id, 0, $length - 6) . substr(md5(uniqid()), 0, 6);
            }
        } while (self::isDuplicate($id) && $attempts < $maxAttempts);

        // 记录生成的ID到缓存
        self::saveIdToCache($id);

        return $id;
    }

    /**
     * 解析ID，返回各部分信息
     *
     * @param string $id 要解析的ID
     * @return array 包含各部分信息的数组
     */
    public static function parse(string $id): array
    {
        $length = strlen($id);
        $timestampLength = 8; // 默认时间戳长度
        $machineCodeLength = 6; // 机器码长度

        // 如果ID长度小于时间戳和机器码长度之和，则调整解析方式
        if ($length < $timestampLength + $machineCodeLength) {
            $timestampLength = min($timestampLength, $length);
            $machineCodeLength = min($machineCodeLength, $length - $timestampLength);
        }

        $timestamp = substr($id, 0, $timestampLength);
        $machineCode = substr($id, $timestampLength, $machineCodeLength);
        $randomString = substr($id, $timestampLength + $machineCodeLength);

        // 尝试将时间戳部分从十六进制转为十进制
        if (ctype_xdigit($timestamp)) {
            $timestampDecoded = @hexdec($timestamp);
            // 检查是否为有效的时间戳
            if ($timestampDecoded > 0 && $timestampDecoded < PHP_INT_MAX) {
                $timestampDecoded = date('Y-m-d H:i:s', $timestampDecoded);
            } else {
                $timestampDecoded = 'Invalid timestamp';
            }
        } else {
            $timestampDecoded = 'Not a hex timestamp';
        }

        return [
            'full_id' => $id,
            'length' => $length,
            'timestamp_part' => $timestamp,
            'timestamp_decoded' => $timestampDecoded,
            'machine_code_part' => $machineCode,
            'random_part' => $randomString,
        ];
    }

    /**
     * 验证ID是否重复
     *
     * @param string $id 要验证的ID
     * @return bool 是否重复
     */
    public static function isDuplicate(string $id): bool
    {
        $existingIds = self::loadIdsFromCache();
        return in_array($id, $existingIds);
    }

    /**
     * 从缓存中加载已生成的ID列表
     *
     * @return array 已生成的ID列表
     */
    private static function loadIdsFromCache(): array
    {
        $ids = Cache::get('hash_ids');
        if (!$ids) {
            return [];
        }
        return $ids;
    }

    /**
     * 将ID保存到缓存中
     *
     * @param string $id 要保存的ID
     * @return void
     */
    private static function saveIdToCache(string $id): void
    {
        $Cache = Cache::get('hash_ids');
        if (!$Cache){
            Cache::set('hash_ids',[$id]);
        }else{
            Cache::push('hash_ids',$id);
        }
    }

    /**
     * 获取已生成的ID列表（用于调试）
     *
     * @return array 已生成的ID列表
     */
    public static function getGeneratedIds(): array
    {
        return self::loadIdsFromCache();
    }
}